using System;
using System.Collections.Generic;
using System.Text;
using SlimDX;
using System.Xml;
using Barrage.Global;

namespace Barrage.GUI.Elements
{
    /// <summary>
    /// Event fired by GUINumberSelector instances when the selected value changes
    /// </summary>
    /// <param name="buttonId">Unique Id of the GUIElement instance</param>
    /// <param name="value">Current item value</param>
    public delegate void GUINumberSelectorHandler(string buttonId, int value);
    /// <summary>
    /// A number selector is used to select a value from a specified range
    /// </summary>
    public class GUINumberSelector : GUISelectableElement
    {
        const string XML_WIDTH_ATTRIBUTE = "width";
        const string XML_HEIGHT_ATTRIBUTE = "height";
        const string XML_TEXT_ATTRIBUTE = "text";
        const string XML_VALUE_ATTRIBUTE = "value";
        const string XML_MINVALUE_ATTRIBUTE = "minValue";
        const string XML_MAXVALUE_ATTRIBUTE = "maxValue";

        /// <summary>
        /// Event that fires when the selected value of the item changes
        /// </summary>
        public event GUINumberSelectorHandler OnInteraction;

        private Vector2 size;
        /// <summary>
        /// Element size (for click detection)
        /// </summary>
        public Vector2 Size { get { return size; } }

        private int value = 0;
        /// <summary>
        /// Current value
        /// </summary>
        public int Value { get { return value; } set { this.value = value; } }

        int maxValue=int.MinValue;
        int minValue=int.MaxValue;
        /// <summary>
        /// Maximum selector value
        /// </summary>        
        public int MaxValue { get { return maxValue; } set { this.maxValue = value; } }
        /// <summary>
        /// Minimum selector value
        /// </summary>        
        public int MinValue { get { return minValue; } set { this.minValue = value; } }
                
        private bool hovering = false;
        /// <summary>
        /// Returns true if the mouse cursor is over the element
        /// </summary>
        public bool Hovering { get { return hovering; } }

        private string text;
        /// <summary>
        /// Element text
        /// </summary>
        public virtual string Text { get { return text; } set { text = value; } }

        /// <summary>
        /// Element default size when no initial size is specified. Can be overriden by inheriting classes
        /// </summary>
        protected virtual Vector2 DefaultSize { get { return new Vector2(100, 100); } }

        /// <summary>
        /// Item constructor
        /// </summary>
        /// <param name="layout">Layout that will hold this item</param>
        /// <param name="template">Template that will be used to render this item</param>
        /// <param name="xn">Xml Node that holds attribute values of this item</param>
        public GUINumberSelector(GUILayout layout, string template, XmlNode xn)
            : base(layout, template, xn)
        {
            size = XmlHelper.ParseXMLVector2Attribute(xn,XML_WIDTH_ATTRIBUTE, XML_HEIGHT_ATTRIBUTE, DefaultSize);
            text = Language.LanguageManager.Texts[XmlHelper.GetXMLAttribute(xn, XML_TEXT_ATTRIBUTE)];
            value = XmlHelper.ParseXMLIntAttribute(xn, XML_VALUE_ATTRIBUTE);
            minValue = XmlHelper.ParseXMLIntAttribute(xn, XML_MINVALUE_ATTRIBUTE,int.MinValue);
            maxValue = XmlHelper.ParseXMLIntAttribute(xn, XML_MAXVALUE_ATTRIBUTE, int.MaxValue);
            
        }

        /// <summary>
        /// \internal Updates item matrices and interaction logic
        /// </summary>
        protected override void DoUpdate()
        {
            base.DoUpdate();
            hovering = CheckIntersection();
            if (hovering) Layout.SelectedItemIndex = Order;
            CheckClick();
            if (Selected)
            {
                if (Keyboard.Instance.KeyPressed(SlimDX.DirectInput.Key.RightArrow) || Gamepad.AnyButtonStatus(SlimDX.XInput.GamepadButtonFlags.DPadRight) || Gamepad.AnyStickPressed(Stick.LeftStick, StickDirection.Right))
                    SelectNext();
                if (Keyboard.Instance.KeyPressed(SlimDX.DirectInput.Key.LeftArrow) || Gamepad.AnyButtonStatus(SlimDX.XInput.GamepadButtonFlags.DPadLeft) || Gamepad.AnyStickPressed(Stick.LeftStick, StickDirection.Left))
                    SelectPrevious();
            }
        }

        /// <summary>
        /// Selects next value in range, if possible
        /// </summary>
        protected void SelectNext()
        {
            if (Value < MaxValue)
            {
                Value += 1;
                SendEvent();
            }
        }

        /// <summary>
        /// Selects previous value in range, if possible
        /// </summary>
        protected void SelectPrevious()
        {
            if (Value > MinValue)
            {
                Value -= 1;
                SendEvent();
            }
        }

        /// <summary>
        /// Invokes an OnInteraction Event
        /// </summary>
        protected void SendEvent()
        {
            if (this.OnInteraction != null)
                this.OnInteraction.Invoke(Id, value);
        }

        /// <summary>
        /// Checks if a mouse button was pressed, selecting a new value in that case
        /// </summary>
        protected void CheckClick()
        {
            if (Mouse.Instance.ButtonReleased() && hovering)
            {
                SelectNext();
            }
            if (Mouse.Instance.ButtonReleased(1) && hovering)
            {
                SelectPrevious();
            }
        }

        /// <summary>
        /// Checks for an intersection between the mouse cursor and the item
        /// </summary>
        /// <returns>True if there was an intersection, false otherwise</returns>
        protected bool CheckIntersection()
        {
            Vector3 mPos = new Vector3(Mouse.Instance.Position, 0);
            Vector4 tPos = GameMath.VectorAbs(Vector3.Transform(mPos, Matrix.Invert(motion.WorldMatrix)));

            return ((tPos.X <= size.X / 2f) && (tPos.Y <= size.Y / 2f));
        }

        /// <summary>
        /// \internal Default action for item interaction (via keyboard or gamepad)
        /// </summary>
        public override void Interact()
        {
            SelectNext();
        }

        /// <summary>
        /// \internal Clear resources
        /// </summary>
        public override void Dispose()
        {
            base.Dispose();
        }
    }

}
